#include<stdlib.h>
#include <stdio.h>
#include <stdint.h> // uint64_t
#include <Type.h>
#include <GlobalVar.h>
#include <Dict.h>
#include <Function.h>
#include <string.h>
#include "Float.h"

Type FloatType = {
        .type=&TypeType,
        .dict=0,
        .name="Float",
        .print = printFloat,
        .add=addFloat,
        .sub=subFloat,
        .mul=mulFloat,
        .div=divFloat,
        .hash = hashFloat,
        .equal = equalFloat,
        .neq = neqFloat,
        .greater = greaterFloat,
        .less = lessFloat,
        .ge = geFloat,
        .le = leFloat,
        .allocate_instance=float_allocate_instance
};

void float_init(Float * self, double value) {
    memset(self,0,sizeof(Float));
    self->type=&FloatType;
    self->dict=dict_allocate_const();
    self->value = value;
}

Float* float_allocate(double f){
    return float_allocate_const(f);
}

Float* float_allocate_const(double f){
    Float* temp = (Float*)malloc(sizeof(Float));
    float_init(temp,f);
    return temp;
}

void float_destroy(Float* self){
    free(self);
}

Object* float_allocate_instance(Object* type){
    return float_allocate(0);
}

void printFloat(Object *self) {
    printf("%.2lf", ((Float *) self)->value);
}

Object *addFloat(Object *self, Object *other) {
    Float *result, *f1, *f2;

    result = malloc(sizeof(Float));
    f1 = (Float *) self;
    f2 = (Float *) other;
    float_init(result, f1->value + f2->value);

    return (Object *) result;
}

Object *subFloat(Object *self, Object *other) {
    Float *result, *f1, *f2;

    result = malloc(sizeof(Float));
    f1 = (Float *) self;
    f2 = (Float *) other;
    float_init(result, f1->value - f2->value);

    return (Object *) result;
}

Object *mulFloat(Object *self, Object *other) {
    Float *result, *f1, *f2;

    result = malloc(sizeof(Float));
    f1 = (Float *) self;
    f2 = (Float *) other;
    float_init(result, f1->value * f2->value);

    return (Object *) result;
}

Object *divFloat(Object *self, Object *other) {
    Float *result, *f1, *f2;

    result = malloc(sizeof(Float));
    f1 = (Float *) self;
    f2 = (Float *) other;
    float_init(result, f1->value / f2->value);

    return (Object *) result;
}

Object *greaterFloat(Object *self, Object *other) {
    Float *f1, *f2;

    f1 = (Float *) self;
    f2 = (Float *) other;

    return f1->value > f2->value ? &True : &False;
}

Object *equalFloat(Object *self, Object *other) {
    Float *f1, *f2;

    f1 = (Float *) self;
    f2 = (Float *) other;

    return f1->value == f2->value ? &True : &False;
}

Object *neqFloat(Object *self, Object *other) {
    Float *f1, *f2;

    f1 = (Float *) self;
    f2 = (Float *) other;

    return f1->value != f2->value ? &True : &False;
}

Object *lessFloat(Object *self, Object *other) {
    Float *f1, *f2;

    f1 = (Float *) self;
    f2 = (Float *) other;

    return f1->value < f2->value ? &True : &False;
}

Object *geFloat(Object *self, Object *other) {
    Float *f1, *f2;

    f1 = (Float *) self;
    f2 = (Float *) other;

    return f1->value >= f2->value ? &True : &False;
}

Object *leFloat(Object *self, Object *other) {
    Float *f1, *f2;

    f1 = (Float *) self;
    f2 = (Float *) other;

    return f1->value <= f2->value ? &True : &False;
}

int hashFloat(Object *self) {
    union {
        uint64_t i;
        double d;
    } x;
    Float *f = (Float *) self;
    x.d = f->value;

    return (int) (x.i ^ (x.i >> 32));
}

Object* pvm_float_init(List* args){
    Float* self = list_get(args,0);
    Float* temp = list_get(args,1);
    self->value=temp->value;
    return &None;
}

void pvm_init_float(){
    FloatType.mro=list_allocate_const();
    FloatType.dict=dict_allocate_const();
    function_insert_native("__init__",pvm_float_init,&FloatType,2,NULL);
}